package com.easily.factory.nfactoryTest;

import com.easily.core.ConfigCenter;
import com.easily.factory.Pool;
import com.easily.factory.Pools;
import com.easily.factory.aop.AopPool;
import com.easily.factory.configure.ConfigurePool;
import com.easily.factory.controller.ControllerPool;
import com.easily.factory.filter.FilterPool;
import com.easily.label.AddPool;
import com.easily.label.ConfigValue;
import com.easily.label.Service;
import com.easily.system.exception.CarrotException;
import com.easily.system.util.AnnotationUtils;
import com.easily.system.util.StringUtils;

import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * beanFactory生产bean、分发bean
 */
public class BeanFactory {
    private static final Logger log = Logger.getGlobal();

    private final ConfigCenter configCenter;
    private final Pools pools = new Pools();
    private List<ObjectBean> objectBeans;

    public BeanFactory(List<ObjectBean> objectBeans,ConfigCenter configCenter) {
        this.objectBeans = objectBeans;
        this.configCenter = configCenter;
    }

    public Pools getPools(){
        return pools;
    }

    public List<ObjectBean> getObjectBeans(){
        return objectBeans;
    }

    public void init(){

        // 注册内置类池
        AopPool aopPool = new AopPool();
        pools.add(aopPool);
        pools.add(new ControllerPool());
        pools.add(new FilterPool());
        pools.add(new ConfigurePool());

        // bean生产一阶段（实例化、初始化bean信息）
        beanOneStage(new ArrayList<>(),aopPool);
        // bean生产二阶段（aop代理、属性注入）
        beanTwoStage(aopPool);

        // 分发类
        distributeClass();

        pools.end();
    }

    /**
     * 分发类
     */
    private void distributeClass() {
        objectBeans.forEach(objectBean ->

                // 循环池列表，找到该clazz属于哪个池
                pools.getAll().values().stream()
                        .filter(pool -> AnnotationUtils.hasAnnotation(objectBean.getClazz(), pool.getLabel()))
                        .forEach(pool -> {
                            pool.filter(objectBean);
                            objectBean.setLabelClass(pool.getClass());
                        })

        );
    }

    /**
     * 二阶段的bean：aop代理、注入属性
     */
    private void beanTwoStage(AopPool aopPool) {
        // aop代理
        for (ObjectBean objectBean:objectBeans) {
            aopProxy(objectBean,aopPool);
        }

        // 属性注入
        for (ObjectBean objectBean:objectBeans){
            // 循环objectBean所有字段，找到注入值
            for (Field field : objectBean.getClazz().getDeclaredFields()) {
                // 判断是有继承自Resource的注解
                Annotation resourceAnnotation = getResourceAnnotation(field);
                if (resourceAnnotation == null) continue;

                // 如果有，获取需要注入的类名
                field.setAccessible(true);

                try {
                    if (field.isAnnotationPresent(ConfigValue.class)) {
                        putConfigValue(objectBean.getBean(), field);
                    } else {
                        putContainerValue(objectBean.getBean(), field, resourceAnnotation);
                    }
                }catch (IllegalAccessException e){
                    throw new CarrotException("属性注入失败:"+e.getMessage());
                }
            }
        }
    }
    /**
     * 判断字段是否有Resource注解
     */
    private Annotation getResourceAnnotation(Field field) {
        for (Annotation annotation : field.getAnnotations()) {
            if(annotation instanceof Resource){
                return annotation;
            }else{
                Resource resource = annotation.annotationType().getAnnotation(Resource.class);
                if (resource != null){
                    return annotation;
                }
            }
        }
        return null;
    }

    /**
     * 获取注解的value
     */
    private String getAnnotationValue(Annotation annotation) {
        try {
            Class<? extends Annotation> annotationClass = annotation.getClass();
            Method method = annotationClass.getMethod("value");
            return (String) method.invoke(annotation);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            return null;
        }
    }

    /**
     * 注入来自配置中心的值
     */
    private void putConfigValue(Object obj,Field field) throws IllegalAccessException {
        Class<?> type = field.getType();
        ConfigValue annotation = field.getAnnotation(ConfigValue.class);
        String value = annotation.value();
        field.set(obj, this.configCenter.get(value, type));
    }

    private void putContainerValue(Object object,Field field,Annotation resourceAnnotation) throws IllegalAccessException {
        String className = getAnnotationValue(resourceAnnotation);
        Class<?> type = field.getType();
        if (className == null || "".equals(className) ) {
            className = type.getName();
        }

        // 获取类名对应的对象
        Object objectValue = getObject(className);

        // 注入
        if(objectValue == null) throw new RuntimeException("类："+object.getClass().getName()
                +" 中的字段："+field.getName()
                +" ，找不到注入值：" +className);
        field.set(object, objectValue);
    }

    /**
     * 获取已经初始化的对象
     */
    private Object getObject(String name) {
        for (ObjectBean objectBean : objectBeans) {
            if (objectBean.getName().equals(name)) return objectBean.getBean();
        }
        return null;
    }

    /**
     * 代理目标对象
     */
    private void aopProxy(ObjectBean objectBean,AopPool aopPool){
        String name = objectBean.getName();
        Class<?> aClass;
        try {
            aClass = Class.forName(name);
        }catch (Exception e){
            throw new CarrotException("一个错误的beanName："+name);
        }
        Object proxy = aopPool.getProxy(aClass, objectBean.getBean());
        if(proxy == null || proxy == objectBean.getObject()){

        }else{
            objectBean.setProxyObject(proxy);
        }
    }

    /**
     * 一阶段的bean：实例化类型、初始化基本参数
     */
    private void beanOneStage(List<ObjectBean> objectBeans2,AopPool aopPool){
        // 一阶段的bean：实例化类型、初始化基本参数
        for (ObjectBean objectBean:objectBeans){
            // 实例化
            objectBean.newInstance();
            // 注册类池
            if(registerPool(objectBean)) continue;
            // 过滤aop类
            if(aopPool.filter(objectBean)) continue;
            // 初始化beanName
            initBeanName(objectBean);
            // 放入二阶段容器
            objectBeans2.add(objectBean);
        }
        this.objectBeans = objectBeans2;
    }

    /**
     * 注册类池
     */
    private boolean registerPool(ObjectBean objectBean){
        // 头上有注解 @AddPool 且实现了 Pool 接口，就是一个类池
        if(objectBean.getClazz().isAnnotationPresent(AddPool.class)
                && objectBean.getClazz().isAssignableFrom(Pool.class)) {
            // 注册类池
            try {
                pools.add(objectBean.getObject());
                log.fine("注册类池成功：" + objectBean.getClazz().getName());
            } catch (Exception e) {
                e.printStackTrace();
                log.warning("已跳过未正确注册类池：" + objectBean.getClazz().getName());
            }
            return true;
        }
        return false;
    }

    /**
     * 放入重复clazz抛出异常
     */
    private void ifRepeatThrow(String name) {
        for (ObjectBean objectBean : objectBeans) {
            if (name.equals(objectBean.getName()))
                throw new CarrotException("重复BeanName： " + name);
        }
    }

    /**
     * 初始化beanName
     */
    private void initBeanName(ObjectBean objectBean) {
        String name = null;
        // service实现类的beanName是service接口的全名
        if (objectBean.getClazz().isAnnotationPresent(Service.class)) {
            // service实现类：必须实现自某一个接口
            if (objectBean.getClazz().getSuperclass() != null) {
                Class<?>[] interfaces = objectBean.getClazz().getInterfaces();
                // 有且仅一个接口
                if (interfaces.length == 1) {
                    for (Class<?> anInterface : interfaces) {
                        name = anInterface.getName();
                    }
                }
            }
            if (StringUtils.isBlankOrNull(name)){
                throw new RuntimeException(objectBean.getClazz().getName()+" service实现类，必须有且仅有一个接口");
            }
        }

        if (name == null) {
            name = objectBean.getClazz().getName();
        }

        // 重复抛出异常
        ifRepeatThrow(name);
        // 保存beanName
        objectBean.setName(name);
    }
}
